// Unpacker.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <process.h>
#include "unzip.h"

// Global Variables:
HINSTANCE hInst;
#define MAX_BUFFER_SIZE	4096

void RemoveCompleteDirectory(TCHAR* path)
{
	WIN32_FIND_DATA find;
	HANDLE hndle;
	TCHAR strFindFiles[600];
	StringCchCopy(strFindFiles, 600, path);
	StringCchCat(strFindFiles, 600, _T("\\*.*"));
	hndle = FindFirstFile(strFindFiles,&find);

	while (hndle != INVALID_HANDLE_VALUE) 
	{
		TCHAR strFolderItem[600];
		StringCchCopy(strFolderItem,600,path);
		StringCchCat(strFolderItem,600,_T("\\"));
		StringCchCat(strFolderItem,600,find.cFileName);


		if ((!lstrcmp(find.cFileName,_T(".")))||(!lstrcmp(find.cFileName,_T("..")))) 
		{
			RemoveDirectory(strFolderItem);

			if (FindNextFile(hndle,&find)) {
				continue;}

			else {
				RemoveDirectory(path);
				break; }
		}


		if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {	
			RemoveCompleteDirectory(strFolderItem);	}
		
		else {
			SetFileAttributes(strFolderItem,FILE_ATTRIBUTE_NORMAL);
			DeleteFile(strFolderItem);}

		if (FindNextFile(hndle,&find)) continue;
		else break;
	}

	FindClose(hndle);
	SetFileAttributes(path,FILE_ATTRIBUTE_DIRECTORY);
	RemoveDirectory(path);
}

INT CALLBACK DefaultDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_INITDIALOG :
        {
            break;
        }
	case WM_DESTROY :
		break;
        
	case WM_CLOSE:
		{
			EndDialog(hwndDlg, IDCANCEL);
			break;
		}

    default:
        break;
    }
    return 0;
}

DWORD* m_pdwCrc32Table;

void ComputeCRCTable()
{
	// This is the official polynomial used by CRC32 in PKZip.
	// Often times the polynomial shown reversed as 0x04C11DB7.
	DWORD dwPolynomial = 0xEDB88320;
	int i, j;

	m_pdwCrc32Table = new DWORD[256];

	DWORD dwCrc;
	for(i = 0; i < 256; i++)
	{
		dwCrc = i;
		for(j = 8; j > 0; j--)
		{
			if(dwCrc & 1)
				dwCrc = (dwCrc >> 1) ^ dwPolynomial;
			else
				dwCrc >>= 1;
		}
		m_pdwCrc32Table[i] = dwCrc;
	}
}

void CalcCrc32(const BYTE byte, DWORD &dwCrc32)
{

	dwCrc32 = ((dwCrc32) >> 8) ^ m_pdwCrc32Table[(byte) ^ ((dwCrc32) & 0x000000FF)];

}

void FreeCRCTable()
{
	delete m_pdwCrc32Table;
	m_pdwCrc32Table = NULL;
}

DWORD ComputeCRC(TCHAR* szFilename)
{
	DWORD dwCrc32 = 0;
	DWORD dwErrorCode = NO_ERROR;
	HANDLE hFile = NULL;

	dwCrc32 = 0xFFFFFFFF;
	ComputeCRCTable();

	// Open the file
	hFile = CreateFile(szFilename,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN,
		NULL);
	if(hFile == INVALID_HANDLE_VALUE)
		dwErrorCode = GetLastError();
	else
	{
		BYTE buffer[MAX_BUFFER_SIZE];
		DWORD dwBytesRead, dwLoop;
		BOOL bSuccess = ReadFile(hFile, buffer, sizeof(buffer), &dwBytesRead, NULL);
		while(bSuccess && dwBytesRead)
		{
			for(dwLoop = 0; dwLoop < dwBytesRead; dwLoop++)
				CalcCrc32(buffer[dwLoop], dwCrc32);
			bSuccess = ReadFile(hFile, buffer, sizeof(buffer), &dwBytesRead, NULL);
		}
	}

	if(hFile != NULL) CloseHandle(hFile);

	dwCrc32 = ~dwCrc32;
	FreeCRCTable();

	return dwCrc32;
}

int APIENTRY _tWinMain(HINSTANCE	hInstance,
						HINSTANCE	hPrevInstance,
						LPTSTR		lpCmdLine,
						int			nCmdShow)
{
	hInst = hInstance;

	//we don't want multiple instances
	if(OpenMutex(MUTEX_ALL_ACCESS, FALSE, _T("ATLANTIS_SFX")) != NULL)
	{
		MessageBox(NULL, _T("Another instance of this extractor is running. Please wait until it finishes."), _T("ActiSetup"), MB_ICONSTOP);
		return 0;
	}

	CreateMutex(NULL, TRUE, _T("ATLANTIS_SFX"));

	//open self
	TCHAR szThisFile[1000];
	GetModuleFileName(hInst, szThisFile, 1000);
	HANDLE hSelf = CreateFile(szThisFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if(hSelf == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, _T("Could not open this file for extraction."), _T("Error"), MB_ICONSTOP);
		return 1;
	}
	//read, CHAR[8] by CHAR[8], until 'DAS30SFX' found
	DWORD nRead = 8;
	BYTE lpBytes[8];
	BOOL bFound = FALSE;
	while(nRead == 8 && !bFound)
	{
		ReadFile(hSelf, &lpBytes, 8, &nRead, NULL);
		if(lpBytes[0] == 'D' && lpBytes[1] == 'A' && lpBytes[2] == 'S' && lpBytes[3] == '3' &&
			lpBytes[4] == '0' && lpBytes[5] == 'S' && lpBytes[6] == 'F' && lpBytes[7] == 'X')
		{
			bFound = TRUE;
		}
	}
	if(!bFound)
	{
		CloseHandle(hSelf);
		MessageBox(NULL, _T("Could not find the internal archive for extraction."), _T("Error"), MB_ICONSTOP);
		return 1;
	}
	TCHAR szArcFile[1000];
	TCHAR szTempPath[1000];
	GetTempPath(1000, szTempPath);
	StringCchPrintf(szArcFile, 1000, _T("%sDAS30SFX.zip"), szTempPath);
	HANDLE hArc = CreateFile(szArcFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
	if(hArc == INVALID_HANDLE_VALUE)
	{
		CloseHandle(hSelf);
		MessageBox(NULL, _T("Could not create the temporary ZIP file."), _T("Error"), MB_ICONSTOP);
		return 1;
	}
	nRead = 4;
	DWORD dwArcSize = 0;
	ReadFile(hSelf, (LPVOID)&dwArcSize, 4, &nRead, NULL);
	nRead = 4;
	DWORD dwHash = 0;
	ReadFile(hSelf, (LPVOID)&dwHash, 4, &nRead, NULL);
	nRead = 512;
	DWORD dwRead = 0;
	BYTE lpBuffer[512];
	while(nRead != 0)
	{
		ReadFile(hSelf, &lpBuffer, 512, &nRead, NULL);
		DWORD nWrote = nRead;
		WriteFile(hArc, &lpBuffer, nRead, &nWrote, NULL);
		if(nWrote != nRead)
		{
			CloseHandle(hArc);
			CloseHandle(hSelf);
			MessageBox(NULL, _T("Could not write to temporary file. Possibly out of disk space."), _T("Error"), MB_ICONSTOP);
			return 1;
		}
	}
	SetFilePointer(hArc, dwArcSize, NULL, FILE_BEGIN);
	SetEndOfFile(hArc);
	CloseHandle(hSelf);
	CloseHandle(hArc);
	DWORD dwHashish = ComputeCRC(szArcFile);
	if(dwHash != dwHashish)
	{
		MessageBox(NULL, _T("Cannot verify CRC. The setup package is corrupt or incomplete."), _T("Error"), MB_ICONSTOP);
		DeleteFile(szArcFile);
		return 1;
	}
	//unzip this archive to temporary folder
	TCHAR szExtPath[1000];
	StringCchPrintf(szExtPath, 1000, _T("%sDAS30SFX"), szTempPath);
	TCHAR szSetupPath[1000];
	TCHAR szSetupPath2[1000];
	StringCchPrintf(szSetupPath, 1000, _T("%s\\SETUP.CMD"), szExtPath);
	StringCchPrintf(szSetupPath2, 1000,  _T("CMD /C \"%s\\SETUP.CMD\""), szExtPath);
	DeleteFile(szSetupPath);
	HWND dlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_UNPACKER_DIALOG), NULL, &DefaultDlgProc);
	ShowWindow(dlg, SW_SHOWDEFAULT);
	UpdateWindow(dlg);
	HZIP zip = OpenZip(szArcFile, NULL);
	ZIPENTRY ze;
	GetZipItem(zip, -1, &ze);
	int numItems = ze.index;
	for(int zi = 0; zi < numItems; zi++)
	{
		ZIPENTRY ze;
		GetZipItem(zip, zi, &ze);
		TCHAR szFilePath[1000];
		StringCchPrintf(szFilePath, 1000, _T("%s\\%s"), szExtPath, ze.name);
		//MessageBox(NULL, ze.name, _T("Extracting..."), MB_OK);
		UnzipItem(zip, zi, szFilePath);
	}
	CloseZip(zip);
	//delete temporary zip file
	DeleteFile(szArcFile);
	DestroyWindow(dlg);
	SetCurrentDirectory(szExtPath);
	if(!PathFileExists(szSetupPath))
	{
		MessageBox(NULL, _T("Cannot find SETUP.CMD. The setup package is corrupt or incomplete."), _T("Error"), MB_ICONSTOP);
		return 1;
	}
	STARTUPINFO si;
	PROCESS_INFORMATION p_i;
	SecureZeroMemory(&si, sizeof(STARTUPINFO));
	SecureZeroMemory(&p_i, sizeof(PROCESS_INFORMATION));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = 0;
	
	CreateProcess(NULL, szSetupPath2, NULL, NULL, TRUE, 0, NULL, szExtPath, &si, &p_i);
	/*SHELLEXECUTEINFO si;
	SecureZeroMemory(&si, sizeof(SHELLEXECUTEINFO));
	si.cbSize = sizeof(SHELLEXECUTEINFO);
	si.lpDirectory = szExtPath;
	si.lpFile = _T("Setup.exe");
	si.nShow = SW_SHOWDEFAULT;
	si.fMask = SEE_MASK_NOCLOSEPROCESS;
	ShellExecuteEx(&si);*/
	WaitForSingleObject(p_i.hProcess, INFINITE);
	RemoveCompleteDirectory(szExtPath);
	return 0;
}
